
package org.drip.service.json;

/*
 * -*- mode: java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 */

/*!
 * Copyright (C) 2016 Lakshmi Krishnamurthy
 * Copyright (C) 2015 Lakshmi Krishnamurthy
 * 
 *  This file is part of DRIP, a free-software/open-source library for fixed income analysts and developers -
 * 		http://www.credit-trader.org/Begin.html
 * 
 *  DRIP is a free, full featured, fixed income rates, credit, and FX analytics library with a focus towards
 *  	pricing/valuation, risk, and market making.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *   	you may not use this file except in compliance with the License.
 *   
 *  You may obtain a copy of the License at
 *  	http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  	distributed under the License is distributed on an "AS IS" BASIS,
 *  	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  
 *  See the License for the specific language governing permissions and
 *  	limitations under the License.
 */

/**
 * ProductValuationProcessor Sets Up and Executes a JSON Based In/Out Product Valuation Processor.
 *
 * @author Lakshmi Krishnamurthy
 */

public class ProductValuationProcessor {

	/**
	 * JSON Based in/out Funding Deposit Thunker
	 * 
	 * @param jsonParameter JSON Funding Deposit Request Parameters
	 * 
	 * @return JSON Funding Deposit Response
	 */

	@SuppressWarnings ("unchecked") static final java.lang.String FundingDepositThunker (
		final org.drip.json.simple.JSONObject jsonParameter)
	{
		org.drip.state.discount.MergedDiscountForwardCurve dcFunding =
			org.drip.service.json.LatentStateProcessor.FundingCurve (jsonParameter);

		if (null == dcFunding) return null;

		org.drip.param.market.CurveSurfaceQuoteContainer csqc = new
			org.drip.param.market.CurveSurfaceQuoteContainer();

		if (!csqc.setFundingState (dcFunding)) return null;

		org.drip.analytics.date.JulianDate dtSpot = dcFunding.epoch();

		org.drip.product.rates.SingleStreamComponent deposit =
			org.drip.service.template.OTCInstrumentBuilder.FundingDeposit (dtSpot, dcFunding.currency(),
				org.drip.json.parser.Converter.StringEntry (jsonParameter, "DepositMaturity"));

		if (null == deposit) return null;

		java.util.Map<java.lang.String, java.lang.Double> mapResult = deposit.value
			(org.drip.param.valuation.ValuationParams.Spot (dtSpot.julian()), null, csqc, null);

		if (null == mapResult) return null;

		org.drip.json.simple.JSONObject jsonResponse = new org.drip.json.simple.JSONObject();

		for (java.util.Map.Entry<java.lang.String, java.lang.Double> me : mapResult.entrySet())
			jsonResponse.put (me.getKey(), me.getValue());

		org.drip.json.simple.JSONArray jsonCashFlowArray = new org.drip.json.simple.JSONArray();

		for (org.drip.analytics.cashflow.CompositePeriod cp : deposit.couponPeriods()) {
			org.drip.json.simple.JSONObject jsonCashFlow = new org.drip.json.simple.JSONObject();

			try {
				jsonCashFlow.put ("StartDate", new org.drip.analytics.date.JulianDate
					(cp.startDate()).toString());

				jsonCashFlow.put ("EndDate", new org.drip.analytics.date.JulianDate
					(cp.endDate()).toString());

				jsonCashFlow.put ("PayDate", new org.drip.analytics.date.JulianDate
					(cp.payDate()).toString());

				jsonCashFlow.put ("FixingDate", new org.drip.analytics.date.JulianDate
					(cp.fxFixingDate()).toString());

				jsonCashFlow.put ("CouponDCF", cp.couponDCF());

				jsonCashFlow.put ("PayDiscountFactor", cp.df (csqc));
			} catch (java.lang.Exception e) {
				e.printStackTrace();

				return null;
			}

			jsonCashFlow.put ("BaseNotional", cp.baseNotional());

			jsonCashFlow.put ("Tenor", cp.tenor());

			jsonCashFlow.put ("FundingLabel", cp.fundingLabel().fullyQualifiedName());

			jsonCashFlow.put ("ForwardLabel", cp.forwardLabel().fullyQualifiedName());

			jsonCashFlow.put ("ReferenceRate", cp.couponMetrics (dtSpot.julian(), csqc).rate());

			jsonCashFlowArray.add (jsonCashFlow);
		}

		jsonResponse.put ("FloatingCashFlow", jsonCashFlowArray);

		return jsonResponse.toJSONString();
	}

	/**
	 * JSON Based in/out Funding Futures Thunker
	 * 
	 * @param jsonParameter JSON Funding Futures Request Parameters
	 * 
	 * @return JSON Funding Futures Response
	 */

	@SuppressWarnings ("unchecked") static final java.lang.String FundingFuturesThunker (
		final org.drip.json.simple.JSONObject jsonParameter)
	{
		org.drip.state.discount.MergedDiscountForwardCurve dcFunding =
			org.drip.service.json.LatentStateProcessor.FundingCurve (jsonParameter);

		if (null == dcFunding) return null;

		org.drip.param.market.CurveSurfaceQuoteContainer csqc = new
			org.drip.param.market.CurveSurfaceQuoteContainer();

		if (!csqc.setFundingState (dcFunding)) return null;

		org.drip.analytics.date.JulianDate dtSpot = dcFunding.epoch();

		org.drip.product.rates.SingleStreamComponent futures =
			org.drip.service.template.ExchangeInstrumentBuilder.ForwardRateFutures (dtSpot.addTenor
				(org.drip.json.parser.Converter.StringEntry (jsonParameter, "FuturesEffectiveTenor")),
					dcFunding.currency());

		if (null == futures) return null;

		java.util.Map<java.lang.String, java.lang.Double> mapResult = futures.value
			(org.drip.param.valuation.ValuationParams.Spot (dtSpot.julian()), null, csqc, null);

		if (null == mapResult) return null;

		org.drip.json.simple.JSONObject jsonResponse = new org.drip.json.simple.JSONObject();

		for (java.util.Map.Entry<java.lang.String, java.lang.Double> me : mapResult.entrySet())
			jsonResponse.put (me.getKey(), me.getValue());

		org.drip.json.simple.JSONArray jsonCashFlowArray = new org.drip.json.simple.JSONArray();

		for (org.drip.analytics.cashflow.CompositePeriod cp : futures.couponPeriods()) {
			org.drip.json.simple.JSONObject jsonCashFlow = new org.drip.json.simple.JSONObject();

			try {
				jsonCashFlow.put ("StartDate", new org.drip.analytics.date.JulianDate
					(cp.startDate()).toString());

				jsonCashFlow.put ("EndDate", new org.drip.analytics.date.JulianDate
					(cp.endDate()).toString());

				jsonCashFlow.put ("PayDate", new org.drip.analytics.date.JulianDate
					(cp.payDate()).toString());

				jsonCashFlow.put ("FixingDate", new org.drip.analytics.date.JulianDate
					(cp.fxFixingDate()).toString());

				jsonCashFlow.put ("CouponDCF", cp.couponDCF());

				jsonCashFlow.put ("PayDiscountFactor", cp.df (csqc));
			} catch (java.lang.Exception e) {
				e.printStackTrace();

				return null;
			}

			jsonCashFlow.put ("BaseNotional", cp.baseNotional());

			jsonCashFlow.put ("Tenor", cp.tenor());

			jsonCashFlow.put ("FundingLabel", cp.fundingLabel().fullyQualifiedName());

			jsonCashFlow.put ("ForwardLabel", cp.forwardLabel().fullyQualifiedName());

			jsonCashFlow.put ("ReferenceRate", cp.couponMetrics (dtSpot.julian(), csqc).rate());

			jsonCashFlowArray.add (jsonCashFlow);
		}

		jsonResponse.put ("FloatingCashFlow", jsonCashFlowArray);

		return jsonResponse.toJSONString();
	}

	/**
	 * JSON Based in/out Funding Fix Float Thunker
	 * 
	 * @param jsonParameter JSON Funding Fix Float Request Parameters
	 * 
	 * @return JSON Funding Fix Float Response
	 */

	@SuppressWarnings ("unchecked") static final java.lang.String FundingFixFloatThunker (
		final org.drip.json.simple.JSONObject jsonParameter)
	{
		org.drip.state.discount.MergedDiscountForwardCurve dcFunding =
			org.drip.service.json.LatentStateProcessor.FundingCurve (jsonParameter);

		if (null == dcFunding) return null;

		org.drip.param.market.CurveSurfaceQuoteContainer csqc = new
			org.drip.param.market.CurveSurfaceQuoteContainer();

		if (!csqc.setFundingState (dcFunding)) return null;

		org.drip.analytics.date.JulianDate dtSpot = dcFunding.epoch();

		org.drip.product.rates.FixFloatComponent irs = null;

		try {
			irs = org.drip.service.template.OTCInstrumentBuilder.FixFloatStandard (dtSpot,
				dcFunding.currency(), "ALL", org.drip.json.parser.Converter.StringEntry (jsonParameter,
					"FixFloatMaturity"), "MAIN", org.drip.json.parser.Converter.DoubleEntry (jsonParameter,
						"FixFloatCoupon"));
		} catch (java.lang.Exception e) {
			e.printStackTrace();

			return null;
		}

		if (null == irs) return null;

		java.util.Map<java.lang.String, java.lang.Double> mapResult = irs.value
			(org.drip.param.valuation.ValuationParams.Spot (dtSpot.julian()), null, csqc, null);

		if (null == mapResult) return null;

		org.drip.json.simple.JSONObject jsonResponse = new org.drip.json.simple.JSONObject();

		for (java.util.Map.Entry<java.lang.String, java.lang.Double> me : mapResult.entrySet())
			jsonResponse.put (me.getKey(), me.getValue());

		org.drip.json.simple.JSONArray jsonFixedCashFlowArray = new org.drip.json.simple.JSONArray();

		for (org.drip.analytics.cashflow.CompositePeriod cp : irs.referenceStream().cashFlowPeriod()) {
			org.drip.json.simple.JSONObject jsonCashFlow = new org.drip.json.simple.JSONObject();

			try {
				jsonCashFlow.put ("StartDate", new org.drip.analytics.date.JulianDate
					(cp.startDate()).toString());

				jsonCashFlow.put ("EndDate", new org.drip.analytics.date.JulianDate
					(cp.endDate()).toString());

				jsonCashFlow.put ("PayDate", new org.drip.analytics.date.JulianDate
					(cp.payDate()).toString());

				jsonCashFlow.put ("FixingDate", new org.drip.analytics.date.JulianDate
					(cp.fxFixingDate()).toString());

				jsonCashFlow.put ("CouponDCF", cp.couponDCF());

				jsonCashFlow.put ("PayDiscountFactor", cp.df (csqc));
			} catch (java.lang.Exception e) {
				e.printStackTrace();

				return null;
			}

			jsonCashFlow.put ("BaseNotional", cp.baseNotional());

			jsonCashFlow.put ("Tenor", cp.tenor());

			jsonCashFlow.put ("FundingLabel", cp.fundingLabel().fullyQualifiedName());

			jsonCashFlow.put ("ReferenceRate", cp.couponMetrics (dtSpot.julian(), csqc).rate());

			jsonFixedCashFlowArray.add (jsonCashFlow);
		}

		jsonResponse.put ("FixedCashFlow", jsonFixedCashFlowArray);

		org.drip.json.simple.JSONArray jsonFloatingCashFlowArray = new org.drip.json.simple.JSONArray();

		for (org.drip.analytics.cashflow.CompositePeriod cp : irs.derivedStream().cashFlowPeriod()) {
			org.drip.json.simple.JSONObject jsonCashFlow = new org.drip.json.simple.JSONObject();

			try {
				jsonCashFlow.put ("StartDate", new org.drip.analytics.date.JulianDate
					(cp.startDate()).toString());

				jsonCashFlow.put ("EndDate", new org.drip.analytics.date.JulianDate
					(cp.endDate()).toString());

				jsonCashFlow.put ("PayDate", new org.drip.analytics.date.JulianDate
					(cp.payDate()).toString());

				jsonCashFlow.put ("FixingDate", new org.drip.analytics.date.JulianDate
					(cp.fxFixingDate()).toString());

				jsonCashFlow.put ("CouponDCF", cp.couponDCF());

				jsonCashFlow.put ("PayDiscountFactor", cp.df (csqc));
			} catch (java.lang.Exception e) {
				e.printStackTrace();

				return null;
			}

			jsonCashFlow.put ("BaseNotional", cp.baseNotional());

			jsonCashFlow.put ("Tenor", cp.tenor());

			jsonCashFlow.put ("FundingLabel", cp.fundingLabel().fullyQualifiedName());

			jsonCashFlow.put ("ForwardLabel", cp.forwardLabel().fullyQualifiedName());

			jsonCashFlow.put ("ReferenceRate", cp.couponMetrics (dtSpot.julian(), csqc).rate());

			jsonFloatingCashFlowArray.add (jsonCashFlow);
		}

		jsonResponse.put ("FloatingCashFlow", jsonFloatingCashFlowArray);

		return jsonResponse.toJSONString();
	}

	/**
	 * JSON Based in/out Credit Default Swap Thunker
	 * 
	 * @param jsonParameter JSON Credit Default Swap Request Parameters
	 * 
	 * @return JSON Credit Default Swap Response
	 */

	@SuppressWarnings ("unchecked") static final java.lang.String CreditDefaultSwapThunker (
		final org.drip.json.simple.JSONObject jsonParameter)
	{
		org.drip.state.discount.MergedDiscountForwardCurve dcFunding =
			org.drip.service.json.LatentStateProcessor.FundingCurve (jsonParameter);

		org.drip.state.credit.CreditCurve ccSurvivalRecovery =
			org.drip.service.json.LatentStateProcessor.CreditCurve (jsonParameter, dcFunding);

		if (null == ccSurvivalRecovery) return null;

		org.drip.param.market.CurveSurfaceQuoteContainer csqc = new
			org.drip.param.market.CurveSurfaceQuoteContainer();

		if (!csqc.setFundingState (dcFunding) || !csqc.setCreditState (ccSurvivalRecovery)) return null;

		org.drip.analytics.date.JulianDate dtSpot = dcFunding.epoch();

		org.drip.product.definition.CreditDefaultSwap cds = null;

		try {
			cds = org.drip.service.template.OTCInstrumentBuilder.CDS (dtSpot,
				org.drip.json.parser.Converter.StringEntry (jsonParameter, "CDSMaturity"),
					org.drip.json.parser.Converter.DoubleEntry (jsonParameter, "CDSCoupon"),
						dcFunding.currency(), ((org.drip.state.identifier.CreditLabel)
							(ccSurvivalRecovery.label())).referenceEntity());
		} catch (java.lang.Exception e) {
			e.printStackTrace();

			return null;
		}

		if (null == cds) return null;

		java.util.Map<java.lang.String, java.lang.Double> mapResult = cds.value
			(org.drip.param.valuation.ValuationParams.Spot (dtSpot.julian()), null, csqc, null);

		if (null == mapResult) return null;

		org.drip.json.simple.JSONObject jsonResponse = new org.drip.json.simple.JSONObject();

		for (java.util.Map.Entry<java.lang.String, java.lang.Double> me : mapResult.entrySet())
			jsonResponse.put (me.getKey(), me.getValue());

		org.drip.json.simple.JSONArray jsonCouponFlowArray = new org.drip.json.simple.JSONArray();

		for (org.drip.analytics.cashflow.CompositePeriod cp : cds.couponPeriods()) {
			org.drip.json.simple.JSONObject jsonCouponFlow = new org.drip.json.simple.JSONObject();

			try {
				jsonCouponFlow.put ("StartDate", new org.drip.analytics.date.JulianDate
					(cp.startDate()).toString());

				jsonCouponFlow.put ("EndDate", new org.drip.analytics.date.JulianDate
					(cp.endDate()).toString());

				jsonCouponFlow.put ("PayDate", new org.drip.analytics.date.JulianDate
					(cp.payDate()).toString());

				jsonCouponFlow.put ("CouponDCF", cp.couponDCF());

				jsonCouponFlow.put ("PayDiscountFactor", cp.df (csqc));

				jsonCouponFlow.put ("SurvivalProbability", cp.survival (csqc));
			} catch (java.lang.Exception e) {
				e.printStackTrace();

				return null;
			}

			jsonCouponFlow.put ("BaseNotional", cp.baseNotional());

			jsonCouponFlow.put ("Tenor", cp.tenor());

			jsonCouponFlow.put ("FundingLabel", cp.fundingLabel().fullyQualifiedName());

			jsonCouponFlow.put ("CreditLabel", cp.creditLabel().fullyQualifiedName());

			jsonCouponFlow.put ("ReferenceRate", cp.couponMetrics (dtSpot.julian(), csqc).rate());

			jsonCouponFlowArray.add (jsonCouponFlow);
		}

		jsonResponse.put ("CouponFlow", jsonCouponFlowArray);

		org.drip.json.simple.JSONArray jsonLossFlowArray = new org.drip.json.simple.JSONArray();

		for (org.drip.analytics.cashflow.LossQuadratureMetrics lqm : cds.lossFlow (dtSpot, csqc)) {
			org.drip.json.simple.JSONObject jsonLossFlow = new org.drip.json.simple.JSONObject();

			try {
				jsonLossFlow.put ("StartDate", new org.drip.analytics.date.JulianDate
					(lqm.startDate()).toString());

				jsonLossFlow.put ("EndDate", new org.drip.analytics.date.JulianDate
					(lqm.endDate()).toString());
			} catch (java.lang.Exception e) {
				e.printStackTrace();

				return null;
			}

			jsonLossFlow.put ("StartSurvival", lqm.startSurvival());

			jsonLossFlow.put ("EndSurvival", lqm.endSurvival());

			jsonLossFlow.put ("EffectiveNotional", lqm.effectiveNotional());

			jsonLossFlow.put ("EffectiveRecovery", lqm.effectiveRecovery());

			jsonLossFlow.put ("EffectiveAccrual", lqm.accrualDCF());

			jsonLossFlow.put ("EffectiveDF", lqm.effectiveDF());

			jsonLossFlowArray.add (jsonLossFlow);
		}

		jsonResponse.put ("LossFlow", jsonLossFlowArray);

		return jsonResponse.toJSONString();
	}

	/**
	 * JSON Based in/out Constant Payment Mortgage Bond Thunker
	 * 
	 * @param jsonParameter JSON Pre-payable Mortgage Bond Request Parameters
	 * 
	 * @return JSON Constant Payment Mortgage Bond Response
	 */

	@SuppressWarnings ("unchecked") static final java.lang.String ConstantMortgageBondThunker (
		final org.drip.json.simple.JSONObject jsonParameter)
	{
		java.lang.String strBondName = org.drip.json.parser.Converter.StringEntry (jsonParameter, "Name");

		java.lang.String strCurrency = org.drip.json.parser.Converter.StringEntry (jsonParameter,
			"Currency");

		java.lang.String strDayCount = org.drip.json.parser.Converter.StringEntry (jsonParameter,
			"DayCount");

		org.drip.analytics.date.JulianDate dtEffective = org.drip.json.parser.Converter.DateEntry
			(jsonParameter, "EffectiveDate");

		if (null == dtEffective) return null;

		int iEffectiveDate = dtEffective.julian();

		org.drip.analytics.date.JulianDate dtSettle = org.drip.json.parser.Converter.DateEntry
			(jsonParameter, "SettleDate");

		if (null == dtSettle) return null;

		int iSettleDate = dtSettle.julian();

		int iNumPayment = -1;
		int iPayFrequency = -1;
		double dblCleanPrice = 1.;
		double dblCouponRate = java.lang.Double.NaN;
		double dblBondNotional = java.lang.Double.NaN;
		double dblServiceFeeRate = java.lang.Double.NaN;
		double dblFixedMonthlyAmount = java.lang.Double.NaN;
		double dblBeginPrincipalFactor = java.lang.Double.NaN;

		try {
			iNumPayment = org.drip.json.parser.Converter.IntegerEntry (jsonParameter, "NumPayment");

			dblCouponRate = org.drip.json.parser.Converter.DoubleEntry (jsonParameter, "CouponRate");

			iPayFrequency = org.drip.json.parser.Converter.IntegerEntry (jsonParameter, "PayFrequency");

			dblBondNotional = org.drip.json.parser.Converter.DoubleEntry (jsonParameter, "BondNotional");

			dblServiceFeeRate = org.drip.json.parser.Converter.DoubleEntry (jsonParameter, "ServiceFeeRate");

			dblFixedMonthlyAmount = org.drip.json.parser.Converter.DoubleEntry (jsonParameter,
				"FixedMonthlyAmount");

			dblBeginPrincipalFactor = org.drip.json.parser.Converter.DoubleEntry (jsonParameter,
				"BeginPrincipalFactor");
		} catch (java.lang.Exception e) {
			e.printStackTrace();

			return null;
		}

		org.drip.product.definition.Bond fpmb = org.drip.product.creator.ConstantPaymentBondBuilder.Standard
			(strBondName, dtEffective, strCurrency, iNumPayment, strDayCount, iPayFrequency, dblCouponRate,
				dblServiceFeeRate, dblFixedMonthlyAmount, dblBondNotional);

		if (null == fpmb || fpmb.maturityDate().julian() <= iSettleDate) return null;

		org.drip.json.simple.JSONObject jsonResponse = new org.drip.json.simple.JSONObject();

		org.drip.json.simple.JSONArray jsonCouponFlowArray = new org.drip.json.simple.JSONArray();

		for (org.drip.analytics.cashflow.CompositePeriod cp : fpmb.couponPeriods()) {
			org.drip.json.simple.JSONObject jsonCouponFlow = new org.drip.json.simple.JSONObject();

			org.drip.analytics.output.CompositePeriodCouponMetrics cpcm = cp.couponMetrics (iEffectiveDate,
				null);

			if (null == cpcm) return null;

			double dblPeriodCouponRate = cpcm.rate();

			jsonCouponFlow.put ("FeeRate", dblServiceFeeRate);

			jsonCouponFlow.put ("CouponRate", dblPeriodCouponRate);

			jsonCouponFlow.put ("PrincipalFactor", dblBeginPrincipalFactor);

			try {
				double dblEndPrincipalFactor = fpmb.notional (cp.endDate());

				double dblYieldDF = org.drip.analytics.support.Helper.Yield2DF (iPayFrequency, dblCouponRate,
					org.drip.analytics.daycount.Convention.YearFraction (dtEffective.julian(), cp.endDate(),
						"30/360", false, null, strCurrency));

				jsonCouponFlow.put ("StartDate", new org.drip.analytics.date.JulianDate
					(cp.startDate()).toString());

				jsonCouponFlow.put ("EndDate", new org.drip.analytics.date.JulianDate
					(cp.endDate()).toString());

				jsonCouponFlow.put ("PayDate", new org.drip.analytics.date.JulianDate
					(cp.payDate()).toString());

				double dblCouponDCF = cp.couponDCF();

				jsonCouponFlow.put ("AccrualDays", dblCouponDCF * 365.);

				jsonCouponFlow.put ("AccrualFraction", dblCouponDCF);

				double dblCouponAmount = dblBeginPrincipalFactor * dblPeriodCouponRate * dblCouponDCF *
					dblBondNotional;
				double dblFeeAmount = dblBeginPrincipalFactor * dblServiceFeeRate * dblCouponDCF *
					dblBondNotional;
				double dblPrincipalAmount = (dblBeginPrincipalFactor - dblEndPrincipalFactor) *
					dblBondNotional;
				double dblTotalAmount = dblPrincipalAmount + dblCouponAmount;
				dblBeginPrincipalFactor = dblEndPrincipalFactor;

				jsonCouponFlow.put ("PrincipalAmount", dblPrincipalAmount);

				jsonCouponFlow.put ("CouponAmount", dblCouponAmount);

				jsonCouponFlow.put ("TotalAmount", dblTotalAmount);

				jsonCouponFlow.put ("DiscountFactor", dblYieldDF);

				jsonCouponFlow.put ("FeeAmount", dblFeeAmount);

				jsonCouponFlow.put ("SurvivalFactor", 1.);
			} catch (java.lang.Exception e) {
				e.printStackTrace();

				return null;
			}

			jsonCouponFlowArray.add (jsonCouponFlow);
		}

		jsonResponse.put ("CouponFlow", jsonCouponFlowArray);

		jsonResponse.put ("CleanPrice", 100. * dblCleanPrice);

		jsonResponse.put ("Face", dblBondNotional);

		org.drip.analytics.date.JulianDate dtPreviousCouponDate = fpmb.previousCouponDate (dtSettle);

		if (null != dtPreviousCouponDate) {
			jsonResponse.put ("AccrualDays", dtPreviousCouponDate.julian() - iSettleDate);

			jsonResponse.put ("PreviousCouponDate", dtPreviousCouponDate.toString());
		}

		org.drip.param.valuation.ValuationParams valParams = org.drip.param.valuation.ValuationParams.Spot
			(iSettleDate);

		try {
			double dblAccruedAmount = fpmb.accrued (iSettleDate, null) * dblBondNotional;

			double dblCurrentPrincipal = fpmb.notional (dtPreviousCouponDate.julian()) * dblBondNotional;

			double dblRisk = fpmb.yield01FromPrice (valParams, null, null, dblCleanPrice);

			jsonResponse.put ("Accrued", dblAccruedAmount);

			jsonResponse.put ("Convexity", fpmb.convexityFromPrice (valParams, null, null, dblCleanPrice));

			jsonResponse.put ("CurrentPrincipal", dblCurrentPrincipal);

			jsonResponse.put ("DV01", dblRisk * dblBondNotional);

			jsonResponse.put ("ModifiedDuration", fpmb.modifiedDurationFromPrice (valParams, null, null,
				dblCleanPrice));

			jsonResponse.put ("Notional", dblBondNotional);

			jsonResponse.put ("NPV", dblCurrentPrincipal + dblAccruedAmount);

			jsonResponse.put ("Risk", dblRisk);

			jsonResponse.put ("SettleDate", dtSettle.toString());

			jsonResponse.put ("Total", dblCurrentPrincipal + dblAccruedAmount);

			jsonResponse.put ("Yield", fpmb.yieldFromPrice (valParams, null, null, dblCleanPrice));
		} catch (java.lang.Exception e) {
			e.printStackTrace();

			return null;
		}

		org.drip.state.discount.MergedDiscountForwardCurve dcFunding =
			org.drip.service.json.LatentStateProcessor.FundingCurve (jsonParameter);

		if (null == dcFunding) return jsonResponse.toJSONString();

		org.drip.param.market.CurveSurfaceQuoteContainer csqc = new
			org.drip.param.market.CurveSurfaceQuoteContainer();

		if (!csqc.setFundingState (dcFunding)) return jsonResponse.toJSONString();

		try {
			jsonResponse.put ("DiscountedPrice", fpmb.priceFromDiscountMargin (valParams, csqc, null, 0.));
		} catch (java.lang.Exception e) {
		}

		if (!csqc.setCreditState (org.drip.service.json.LatentStateProcessor.CreditCurve (jsonParameter,
			dcFunding)))
			return jsonResponse.toJSONString();

		try {
			jsonResponse.put ("DiscountedCreditPrice", fpmb.priceFromCreditBasis (valParams, csqc, null,
				0.));
		} catch (java.lang.Exception e) {
		}

		return jsonResponse.toJSONString();
	}

	/**
	 * JSON Based in/out Pre-Payable Mortgage Bond Thunker
	 * 
	 * @param jsonParameter JSON Pre-Payable Mortgage Bond Request Parameters
	 * 
	 * @return JSON Pre-Payable Mortgage Bond Response
	 */

	@SuppressWarnings ("unchecked") static final java.lang.String PrepayableMortgageBondThunker (
		final org.drip.json.simple.JSONObject jsonParameter)
	{
		java.lang.String strBondName = org.drip.json.parser.Converter.StringEntry (jsonParameter, "Name");

		java.lang.String strCurrency = org.drip.json.parser.Converter.StringEntry (jsonParameter,
			"Currency");

		java.lang.String strDayCount = org.drip.json.parser.Converter.StringEntry (jsonParameter,
			"DayCount");

		org.drip.analytics.date.JulianDate dtEffective = org.drip.json.parser.Converter.DateEntry
			(jsonParameter, "EffectiveDate");

		if (null == dtEffective) return null;

		int iEffectiveDate = dtEffective.julian();

		org.drip.analytics.date.JulianDate dtSettle = org.drip.json.parser.Converter.DateEntry
			(jsonParameter, "SettleDate");

		if (null == dtSettle) return null;

		int iSettleDate = dtSettle.julian();

		int iNumPayment = -1;
		int iPayFrequency = -1;
		double dblCleanPrice = 1.;
		double dblCPR = java.lang.Double.NaN;
		double dblCouponRate = java.lang.Double.NaN;
		double dblBondNotional = java.lang.Double.NaN;
		double dblServiceFeeRate = java.lang.Double.NaN;
		double dblFixedMonthlyAmount = java.lang.Double.NaN;
		double dblBeginPrincipalFactor = java.lang.Double.NaN;

		try {
			dblCPR = org.drip.json.parser.Converter.DoubleEntry (jsonParameter, "CPR");

			iNumPayment = org.drip.json.parser.Converter.IntegerEntry (jsonParameter, "NumPayment");

			dblCouponRate = org.drip.json.parser.Converter.DoubleEntry (jsonParameter, "CouponRate");

			iPayFrequency = org.drip.json.parser.Converter.IntegerEntry (jsonParameter, "PayFrequency");

			dblBondNotional = org.drip.json.parser.Converter.DoubleEntry (jsonParameter, "BondNotional");

			dblServiceFeeRate = org.drip.json.parser.Converter.DoubleEntry (jsonParameter, "ServiceFeeRate");

			dblFixedMonthlyAmount = org.drip.json.parser.Converter.DoubleEntry (jsonParameter,
				"FixedMonthlyAmount");

			dblBeginPrincipalFactor = org.drip.json.parser.Converter.DoubleEntry (jsonParameter,
				"BeginPrincipalFactor");
		} catch (java.lang.Exception e) {
			e.printStackTrace();

			return null;
		}

		org.drip.product.definition.Bond fpmb = org.drip.product.creator.ConstantPaymentBondBuilder.Prepay
			(strBondName, dtEffective, strCurrency, iNumPayment, strDayCount, iPayFrequency, dblCouponRate,
				dblServiceFeeRate, dblCPR, dblFixedMonthlyAmount, dblBondNotional);

		if (null == fpmb || fpmb.maturityDate().julian() <= iSettleDate) return null;

		org.drip.json.simple.JSONObject jsonResponse = new org.drip.json.simple.JSONObject();

		org.drip.json.simple.JSONArray jsonCouponFlowArray = new org.drip.json.simple.JSONArray();

		for (org.drip.analytics.cashflow.CompositePeriod cp : fpmb.couponPeriods()) {
			org.drip.json.simple.JSONObject jsonCouponFlow = new org.drip.json.simple.JSONObject();

			org.drip.analytics.output.CompositePeriodCouponMetrics cpcm = cp.couponMetrics (iEffectiveDate,
				null);

			if (null == cpcm) return null;

			double dblPeriodCouponRate = cpcm.rate();

			jsonCouponFlow.put ("FeeRate", dblServiceFeeRate);

			jsonCouponFlow.put ("CouponRate", dblPeriodCouponRate);

			jsonCouponFlow.put ("PrincipalFactor", dblBeginPrincipalFactor);

			try {
				double dblEndPrincipalFactor = fpmb.notional (cp.endDate());

				double dblYieldDF = org.drip.analytics.support.Helper.Yield2DF (iPayFrequency, dblCouponRate,
					org.drip.analytics.daycount.Convention.YearFraction (dtEffective.julian(), cp.endDate(),
						"30/360", false, null, strCurrency));

				jsonCouponFlow.put ("StartDate", new org.drip.analytics.date.JulianDate
					(cp.startDate()).toString());

				jsonCouponFlow.put ("EndDate", new org.drip.analytics.date.JulianDate
					(cp.endDate()).toString());

				jsonCouponFlow.put ("PayDate", new org.drip.analytics.date.JulianDate
					(cp.payDate()).toString());

				double dblCouponDCF = cp.couponDCF();

				jsonCouponFlow.put ("AccrualDays", dblCouponDCF * 365.);

				jsonCouponFlow.put ("AccrualFraction", dblCouponDCF);

				double dblCouponAmount = dblBeginPrincipalFactor * dblPeriodCouponRate * dblCouponDCF *
					dblBondNotional;
				double dblFeeAmount = dblBeginPrincipalFactor * dblServiceFeeRate * dblCouponDCF *
					dblBondNotional;
				double dblPrincipalAmount = (dblBeginPrincipalFactor - dblEndPrincipalFactor) *
					dblBondNotional;
				double dblTotalAmount = dblPrincipalAmount + dblCouponAmount;
				dblBeginPrincipalFactor = dblEndPrincipalFactor;

				jsonCouponFlow.put ("PrincipalAmount", dblPrincipalAmount);

				jsonCouponFlow.put ("CouponAmount", dblCouponAmount);

				jsonCouponFlow.put ("TotalAmount", dblTotalAmount);

				jsonCouponFlow.put ("DiscountFactor", dblYieldDF);

				jsonCouponFlow.put ("FeeAmount", dblFeeAmount);

				jsonCouponFlow.put ("SurvivalFactor", 1.);
			} catch (java.lang.Exception e) {
				e.printStackTrace();

				return null;
			}

			jsonCouponFlowArray.add (jsonCouponFlow);
		}

		jsonResponse.put ("CouponFlow", jsonCouponFlowArray);

		jsonResponse.put ("CleanPrice", 100. * dblCleanPrice);

		jsonResponse.put ("Face", dblBondNotional);

		org.drip.analytics.date.JulianDate dtPreviousCouponDate = fpmb.previousCouponDate (dtSettle);

		if (null != dtPreviousCouponDate) {
			jsonResponse.put ("AccrualDays", dtPreviousCouponDate.julian() - iSettleDate);

			jsonResponse.put ("PreviousCouponDate", dtPreviousCouponDate.toString());
		}

		org.drip.param.valuation.ValuationParams valParams = org.drip.param.valuation.ValuationParams.Spot
			(iSettleDate);

		try {
			double dblAccruedAmount = fpmb.accrued (iSettleDate, null) * dblBondNotional;

			double dblCurrentPrincipal = fpmb.notional (dtPreviousCouponDate.julian()) * dblBondNotional;

			double dblRisk = fpmb.yield01FromPrice (valParams, null, null, dblCleanPrice);

			jsonResponse.put ("Accrued", dblAccruedAmount);

			jsonResponse.put ("Convexity", fpmb.convexityFromPrice (valParams, null, null, dblCleanPrice));

			jsonResponse.put ("CurrentPrincipal", dblCurrentPrincipal);

			jsonResponse.put ("DV01", dblRisk * dblBondNotional);

			jsonResponse.put ("ModifiedDuration", fpmb.modifiedDurationFromPrice (valParams, null, null,
				dblCleanPrice));

			jsonResponse.put ("Notional", dblBondNotional);

			jsonResponse.put ("NPV", dblCurrentPrincipal + dblAccruedAmount);

			jsonResponse.put ("Risk", dblRisk);

			jsonResponse.put ("SettleDate", dtSettle.toString());

			jsonResponse.put ("Total", dblCurrentPrincipal + dblAccruedAmount);

			jsonResponse.put ("Yield", fpmb.yieldFromPrice (valParams, null, null, dblCleanPrice));
		} catch (java.lang.Exception e) {
			e.printStackTrace();

			return null;
		}

		org.drip.state.discount.MergedDiscountForwardCurve dcFunding =
			org.drip.service.json.LatentStateProcessor.FundingCurve (jsonParameter);

		if (null == dcFunding) return jsonResponse.toJSONString();

		org.drip.param.market.CurveSurfaceQuoteContainer csqc = new
			org.drip.param.market.CurveSurfaceQuoteContainer();

		if (!csqc.setFundingState (dcFunding)) return jsonResponse.toJSONString();

		try {
			jsonResponse.put ("DiscountedPrice", fpmb.priceFromDiscountMargin (valParams, csqc, null, 0.));
		} catch (java.lang.Exception e) {
		}

		if (!csqc.setCreditState (org.drip.service.json.LatentStateProcessor.CreditCurve (jsonParameter,
			dcFunding)))
			return jsonResponse.toJSONString();

		try {
			jsonResponse.put ("DiscountedCreditPrice", fpmb.priceFromCreditBasis (valParams, csqc, null,
				0.));
		} catch (java.lang.Exception e) {
		}

		return jsonResponse.toJSONString();
	}
}
